#include "serial.h"

Serial::Serial()
{
        m_iDevice=-1;
}

Serial::~Serial()
{
        CloseDevice();
}

bool Serial::OpenDevice(const char *sNameDevice)
{
        pLogger = Global::GetLogger();
        if(m_iDevice != -1)
                CloseDevice();
        m_iDevice = open(sNameDevice, O_RDWR | O_NOCTTY);
        if(m_iDevice < 0)
        {
                pLogger->LogError("Serial::OpenDevice: Faile to open device %s. %s.\n", sNameDevice, strerror(errno));
                return false;
        }
        tcgetattr(m_iDevice, &m_OldTermios);
        return true;
}

void Serial::CloseDevice()
{
        if(m_iDevice != -1)
        {
                SetTermios(&m_OldTermios);
                close(m_iDevice);
                m_iDevice = -1;
        }
}

bool Serial::SetPort(struct PortInfo *p_PortInfo)
{
	if(m_iDevice == -1)
	{
		pLogger->LogError("Serial::SetPort: you hanv not opened serial.\n");
		return false;
	}
	
	memset(&m_Termios, 0, sizeof(termios));

	cfmakeraw(&m_Termios);

	speed_t iBaudRate = GetBaudRate(p_PortInfo->BaudRate);
	if(iBaudRate == 0xffff)
	{
		pLogger->LogError("Serial::SetPort: Faile to set port, area is in BaudRate.\n");
		return false;
	}
	cfsetispeed(&m_Termios, iBaudRate);
	cfsetospeed(&m_Termios, iBaudRate);

	unsigned int iDataBits = GetDataBits(p_PortInfo->DataBits);
	if(iDataBits == 0xffff)
	{
		pLogger->LogError("Serial::SetPort: Faile to set port, area is in DataBits.\n");
		return false;	
	}
	m_Termios.c_cflag &= ~CSIZE;
	m_Termios.c_cflag |= iDataBits;

	if(SetParity(p_PortInfo->Parity) == false)
	{
		pLogger->LogError("Serial::SetPort: Faile to set port, area is in Parity.\n");
		return false;
	}
        
	if(SetStopBit(p_PortInfo->StopBit) == false)
        {
                pLogger->LogError("Serial::SetPort: Faile to set port, area is in StopBit.\n");
                return false;
        }
	
        if(SetFlowCtrl(p_PortInfo->FlowCtrl) == false)
        {
                pLogger->LogError("Serial::SetPort: Faile to set port, area is in StopBit.\n");
                return false;
        }

	m_Termios.c_cflag |= CREAD;
	m_Termios.c_cflag |= CLOCAL;
	m_Termios.c_cc[VMIN]=1;
	m_Termios.c_cc[VTIME]=10;
///////////////////////////////////////////////////
/*	
	m_Termios.c_cflag |= HUPCL;

	m_Termios.c_oflag |= ONLCR;
	
	m_Termios.c_lflag |= IEXTEN;
	m_Termios.c_lflag |= ECHOK;
	m_Termios.c_lflag |= ECHOCTL;
	m_Termios.c_lflag |= ECHOKE;

	m_Termios.c_cc[0]=0x03;
	m_Termios.c_cc[1]=0x1c;
	m_Termios.c_cc[2]=0x7f;
	m_Termios.c_cc[3]=0x15;
	m_Termios.c_cc[4]=0x04;
	m_Termios.c_cc[5]=0x0a;
	m_Termios.c_cc[6]=0x01;
	m_Termios.c_cc[7]=0x00;
	m_Termios.c_cc[8]=0x11;
	m_Termios.c_cc[9]=0x13;
	m_Termios.c_cc[10]=0x1a;
	m_Termios.c_cc[11]=0x00;
	m_Termios.c_cc[12]=0x12;
	m_Termios.c_cc[13]=0x0f;
	m_Termios.c_cc[14]=0x17;
	m_Termios.c_cc[15]=0x16;
*/
////////////////////////////////////////////

	pLogger->LogDebug("Serial::SetPort: Success to set port:\ntermios.c_iflag=0%o, termios.c_oflag=0%o, termios.c_cflag=0%o\ntermios.c_lflag=0%o, termios.c_line=0%o, termios.c_cc=0%o.\n",m_Termios.c_iflag, m_Termios.c_oflag, m_Termios.c_cflag, m_Termios.c_lflag, m_Termios.c_line, m_Termios.c_cc);

	
	int i;
	for(i=0;i<NCCS;i++)
	{
        	pLogger->LogDebug("c_cc[%d]=%2x  ", i, m_Termios.c_cc[i]);
		if(i%5 == 4)
        		pLogger->LogDebug("\n");
	}
        
	if(SetTermios(&m_Termios) == false)
        {
                pLogger->LogError("Serial::SetPort: Faile to set port, in setting termios.\n");
                return false;
        }

	return true;
}

bool Serial::SetTermios(struct termios *p_Termios)
{
        if(tcsetattr(m_iDevice, TCSADRAIN, p_Termios) != 0)
        {
                pLogger->LogError("Serial::SetTermios: Fail to set termios. %s.\n", strerror(errno));
		return false;
        }
	return true;	
}

bool Serial::SetBaudRate(unsigned int uiBauteRate)
{

	speed_t iBaudRate = GetBaudRate(uiBauteRate);
	if(iBaudRate == 0xffff)
	{
		pLogger->LogError("Serial::SetPort: Faile to set port, area is in BaudRate.\n");
		return false;
	}
	cfsetispeed(&m_Termios, iBaudRate);
	cfsetospeed(&m_Termios, iBaudRate);

	tcflush(m_iDevice, TCIOFLUSH);
	if(tcsetattr(m_iDevice, TCSANOW, &m_Termios) != 0)
	{
		pLogger->LogError("Serial::SetPort: Faile to set port, area is in BaudRate.\n");
		return false;
	}
	tcflush(m_iDevice, TCIOFLUSH);
	return true;
}


speed_t Serial::GetBaudRate(unsigned int iBaudRate)
{
	switch(iBaudRate)
	{
		case 0:
			return B0;
		case 50:
			return B50;
		case 75:
			return B75;
		case 110:
			return B110;
		case 200:
			return B200;
		case 300:
			return B300;
		case 600:
			return B600;
		case 1200:
			return B1200;
		case 1800:
			return B1800;
		case 2400:
			return B2400;
		case 4800:
			return B4800;
		case 9600:
			return B9600;
		case 19200:
			return B19200;
		case 38400:
			return B38400;
		case 57600:
			return B57600;
		case 115200:
			return B115200;
		case 230400:
			return B230400;
		default:
			return 0xffff;
	}
}

unsigned int Serial::GetDataBits(char iDataBits)
{
	switch(iDataBits)
	{
		case 5:
			return CS5;
		case 6:
			return CS6;
		case 7:
			return CS7;
		case 8:
			return CS8;
		default:
			return 0xffff;
	}
}

bool Serial::SetParity(char cParity)
{
	switch(cParity)
	{
		case 'n':
		case 'N':
			m_Termios.c_cflag &= ~PARENB;
			m_Termios.c_iflag &= ~INPCK;
			return true;
		case 'o':
		case 'O':
			m_Termios.c_cflag |= (PARENB | PARODD);
			m_Termios.c_iflag |= INPCK;
			return true;
		case 'e':
		case 'E':
			m_Termios.c_cflag |= PARENB;
			m_Termios.c_cflag &= ~PARODD;
			m_Termios.c_iflag |= INPCK;
			return true;
		default:
			return false;
	}
}


bool Serial::SetStopBit(char iStopBit)
{
        switch(iStopBit)
        {
                case 1:
                        m_Termios.c_cflag &= ~CSTOPB;
                        return true;
                case 2:
                        m_Termios.c_cflag |= CSTOPB;
                        return true;
                default:
                        return false;
        }
}

bool Serial::SetFlowCtrl(char iFlowCtrl)
{
	switch(iFlowCtrl)
	{
		case 0:
			m_Termios.c_cflag &= ~CRTSCTS;
			return true;
		case 1:
			m_Termios.c_cflag |= CRTSCTS;
			return true;	
		case 2:
			m_Termios.c_cflag |= IXON | IXOFF | IXANY;
			return true;
		default:
			return false;
	}
}

int Serial::SetHardwareCtrl(unsigned int HardPin, bool flag)
{
        if(m_iDevice == -1)
        {
                pLogger->LogError("Serial::Read: you hanv not opened serial.\n");
                return -1;
        }

	unsigned int status;
	ioctl(m_iDevice, TIOCMGET, &status);
	if(flag)
		status |= HardPin;
	else
		status &= (~HardPin);
	
	ioctl(m_iDevice, TIOCMSET, &status);
	return 0;	
}

int Serial::Read(unsigned char *pBuff, int iLength)
{
	if(m_iDevice == -1)
        {
                pLogger->LogError("Serial::Read: you hanv not opened serial.\n");
                return -1;
        }
	
	int iReturn;
	iReturn=read(m_iDevice, pBuff, iLength);
	if(iReturn<0)
	{
		pLogger->LogError("Serial::Read: read error. %s\n", strerror(errno));
                return -1;
	}
	return iReturn;
}

int Serial::ReadToTimeout(unsigned char *pBuff, int iLength, struct timeval *tv)
{
	if(m_iDevice == -1)
        {
                pLogger->LogError("Serial::ReadToTimeout: you hanv not opened serial.\n");
                return -1;
        }

	int iReturn;
	fd_set fs_read;
	
	FD_ZERO(&fs_read);
	FD_SET(m_iDevice, &fs_read);
	
        switch(select(m_iDevice+1, &fs_read, NULL, NULL, tv))
	{
	case -1:
                pLogger->LogError("Serial::ReadToTimeout: select. %s\n", strerror(errno));
		return -1;
	case 0:
                pLogger->LogInfo("Serial::ReadToTimeout: read timeout.\n");
		return 0;
        default: 
		iReturn=read(m_iDevice, pBuff, iLength);
	        if(iReturn<0)
	        {
	                pLogger->LogError("Serial::ReadToTimeout: read error. %s\n", strerror(errno));
	                return -1;
        	}
		
                return iReturn;
	}
	pLogger->LogInfo("tv.tv_sec=%d,tv.tv_usec=%d\n",tv->tv_sec, tv->tv_usec);
}

int Serial::ReadSyncToTimeout(unsigned char *pBuff, int iLength, struct timeval *tv)
{
        if(m_iDevice == -1)
        {
                pLogger->LogError("Serial::ReadSyncToTimeout: you hanv not opened serial.\n");
                return -1;
        }

        int iReceived = 0;
	int iReturn;
        fd_set fs_read;

        FD_ZERO(&fs_read);
        FD_SET(m_iDevice, &fs_read);
	
	while(iReceived<iLength)
	{
	        switch(select(m_iDevice+1, &fs_read, NULL, NULL, tv))
	        {
	        case -1:
	                pLogger->LogError("Serial::ReadSyncToTimeout: select. %s\n", strerror(errno));
	                return -1;
	        case 0:
	                pLogger->LogInfo("Serial::ReadSyncToTimeout: read timeout.\n");
	                goto read_stop;
	        default:
	                iReturn=read(m_iDevice, &pBuff[iReceived], iLength-iReceived);
	                if(iReturn<0)
		        {
		                pLogger->LogError("Serial::ReadSyncToTimeout: read error. %s\n", strerror(errno));
                		return -1;
        		}

		        iReceived += iReturn;
	        }
		pLogger->LogInfo("iReceived=%d,tv.tv_sec=%d,tv.tv_usec=%d\n",iReceived,tv->tv_sec, tv->tv_usec);
	}
read_stop:
//	pLogger->LogInfo("tv.tv_sec=%d,tv.tv_usec=%d\n",tv->tv_sec, tv->tv_usec);
	return iReceived;
}

int Serial::Write(unsigned char *pBuff, int iLength)
{
        if(m_iDevice == -1)
        {
                pLogger->LogError("Serial::write: you hanv not opened serial.\n");
                return -1;
        }

	int iReturn;
	iReturn=write(m_iDevice, pBuff, iLength);
	if(iReturn<0)
        {
                pLogger->LogError("Serial::Write: write error. %s\n", strerror(errno));
                return -1;
        }
//	for(int i=0;i<iLength;i++)
//        	pLogger->LogError("%3d",pBuff[i]);
	return iReturn;
}

int Serial::WriteSync(unsigned char *pBuff, int iLength)
{
        if(m_iDevice == -1)
        {
                pLogger->LogError("Serial::write: you hanv not opened serial.\n");
                return -1;
        }

        int iReturn;
	int iSended=0;
	while(iSended<iLength)
	{
        	iReturn=write(m_iDevice, pBuff+iSended, iLength-iSended);
        	if(iReturn<0)
        	{
        	        pLogger->LogError("Serial::Write: write error. %s\n", strerror(errno));
        	        return -1;
        	}
		iSended += iReturn;
	}
        return iSended;
}

int Serial::ClearSerialBuff(int type)
{
        switch(type)
        {
                case 0:
                        return tcflush(m_iDevice, TCIFLUSH);
                case 1:
                        return tcflush(m_iDevice, TCOFLUSH);
                case 2:
                        return tcflush(m_iDevice, TCIOFLUSH);
                default:
                        return -1;
        }
}
